package com.xc.disk.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xc.disk.bean.DownloadBean;
import com.xc.disk.bean.HashBean;
import com.xc.disk.bean.NoticeBean;
import com.xc.disk.bean.SystemFolderBean;
import com.xc.disk.config.Constants;
import com.xc.disk.dto.DownloadDto;
import com.xc.disk.dto.FileDto;
import com.xc.disk.entity.FileEntity;
import com.xc.disk.enums.RedisLock;
import com.xc.disk.exception.OperateException;
import com.xc.disk.info.DeleteInfo;
import com.xc.disk.info.HashInfo;
import com.xc.disk.service.FileService;
import com.xc.disk.service.FolderService;
import com.xc.disk.service.NoticeService;
import com.xc.disk.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>项目名称: Stars</p>
 * <p>描述: 通知服务实现类</p>
 * <P>创建人:小程 </p>
 * <P>创建人邮箱: 779943476@qq.com</p>
 * <p>创建时间: 2019/12/5</p>
 *
 * @version v1.0
 */
@Service
@Slf4j
public class NoticeServiceImpl implements NoticeService {
    @Autowired
    private FileService fileService;
    @Autowired
    private Constants constants;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private FolderService folderService;

    @Override
    public void notice(NoticeBean noticeBean) {
        if (!constants.getAppId().equals(noticeBean.getAppId())) {
            throw new OperateException("f016", "你的appId不正确！");
        }
        //签名
        if (!constants.getAppSecret().equals(noticeBean.getAppSecret())) {
            throw new OperateException("f013", "你的秘钥不正确,请检查秘钥！");
        }
        ResponseEntity<Boolean> resultEntity;
        try {
            String url = noticeBean.getLocalUrl() + constants.getCheckState();
            resultEntity = restTemplate.exchange(url, HttpMethod.GET, null, Boolean.class);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new OperateException("f048", "验证服务地址失败,请检查服务地址是否可访问!");
        }
        if (resultEntity.getStatusCodeValue() != 200 || resultEntity.getBody() == null || !resultEntity.getBody()) {
            throw new OperateException("f048", "传入服务地址访问异常!");
        }
        // 获取缓存数据
        List<NoticeBean> noticeBeans = RedisUtils.get(constants.getFileServerKey());
        // 判断缓存数据是否存在
        if (noticeBeans == null) {
            noticeBeans = new ArrayList<>();
        }
        // 验证文件源是否被使用
        List<String> hashCodes = new ArrayList<>();
        for (HashInfo hashInfo : noticeBean.getHashInfos()) {
            hashCodes.add(hashInfo.getHashCode());
        }
        // 未使用，删除
        folderService.verifyHashCode(hashCodes, 1000, (stringList) -> {
            log.info("删除文件hash值:{}", stringList);
            List<HashInfo> hashInfos = new ArrayList<>(noticeBean.getHashInfos());
            for (String code : stringList) {
                for (HashInfo hashInfo : hashInfos) {
                    if (code.equals(hashInfo.getHashCode())) {
                        noticeBean.getHashInfos().remove(hashInfo);
                    }
                }
            }
            //删除文件
            folderService.deleteSource(stringList, noticeBean.getLocalUrl(), noticeBean.getSecret());
        });

        //替换原缓存数据
        NoticeBean noticeBean2 = null;
        for (NoticeBean notice : noticeBeans) {
            if (notice.getLocalUrl().equals(noticeBean.getLocalUrl())) {
                noticeBean2 = notice;
            }
        }
        if (noticeBean2 != null) {
            noticeBeans.remove(noticeBean2);
        }
        noticeBeans.add(noticeBean);
        //存入redis
        RedisUtils.set(constants.getFileServerKey(), noticeBeans);

        // 检测数据库文件是否存在
        List<String> codes = new ArrayList<>();
        for (NoticeBean notice : noticeBeans) {
            for (HashInfo hashInfo : notice.getHashInfos()) {
                codes.add(hashInfo.getHashCode());
            }
        }
        Thread thread = new Thread() {
            @Override
            public void run() {
                List<String> errorCodes = new ArrayList<>();
                List<FileEntity> entities = fileService.list();
                for (FileEntity fileEntity : entities) {
                    boolean bool = true;
                    for (String code : codes) {
                        if (code.equals(fileEntity.getHashCode())) {
                            bool = false;
                        }
                    }
                    if (bool) {
                        errorCodes.add(fileEntity.getHashCode());
                    }
                }
                log.info("数据库数据异常，异常数据:{}", errorCodes);
            }

        };
        thread.start();
    }

    @Override
    public String noticeSuccess(HashBean hashBean) {
        if (!constants.getAppId().equals(hashBean.getAppId())) {
            throw new OperateException("f016", "你的appId不正确！");
        }
        //签名
        if (!constants.getAppSecret().equals(hashBean.getAppSecret())) {
            throw new OperateException("f013", "你的秘钥不正确,请检查秘钥！");
        }
        //验证文件hash值
        FileDto fileDto = RedisUtils.get(hashBean.getFid());
        if (!fileDto.getHashCode().equals(hashBean.getHashCode())) {
            throw new OperateException("f013", "保存失败，你的文件hash值验证不通过！");
        }
        if (!fileDto.getSize().equals(hashBean.getSize())) {
            throw new OperateException("f051", "保存失败，文件大小验证不通过！");
        }
        //分布式锁，创建文件锁
        try {
            if (!RedisUtils.lock(RedisLock.FILE_LOCK.getKey(), System.currentTimeMillis())) {
                throw new OperateException("", "文件操作超时！");
            }
            //创建文件
            FileEntity fileEntity = fileService.createFileInfo(fileDto);
            List<NoticeBean> noticeBeans = RedisUtils.get(constants.getFileServerKey());
            for (NoticeBean noticeBean : noticeBeans) {
                if (noticeBean.getAppId().equals(hashBean.getAppId())) {
                    noticeBean.getHashInfos().add(new HashInfo(hashBean.getFilePath(), hashBean.getHashCode(), hashBean.getSize()));
                    for (SystemFolderBean systemFolder : noticeBean.getFolderBeans()) {
                        if (systemFolder.getPath().equals(hashBean.getFilePath())) {
                            systemFolder.setAvailableSpace(systemFolder.getAvailableSpace() - hashBean.getSize());
                        }
                    }
                }
            }
            RedisUtils.set(constants.getFileServerKey(), noticeBeans);
            RedisUtils.delete(hashBean.getFid());
            return fileEntity.getId();
        } finally {
            RedisUtils.unlock(RedisLock.FILE_LOCK.getKey());
        }
    }


    @Override
    public DownloadDto noticeDownload(DownloadBean downloadBean) {
        if (!constants.getAppId().equals(downloadBean.getAppId())) {
            throw new OperateException("f016", "你的appId不正确！");
        }
        //签名
        if (!constants.getAppSecret().equals(downloadBean.getAppSecret())) {
            throw new OperateException("f013", "你的秘钥不正确,请检查秘钥！");
        }
        String key = constants.getDownloadKey() + downloadBean.getFid();
        FileEntity fileEntity = RedisUtils.get(key);
        if (fileEntity == null) {
            return null;
        }
        DownloadDto downloadDto = new DownloadDto();
        downloadDto.setHashCode(fileEntity.getHashCode());
        downloadDto.setName(fileEntity.getName());
        return downloadDto;
    }

}
